
/***********************************************************************************************************************
**                                                    Includes                                                        **
***********************************************************************************************************************/
#include <string.h>
#include "CanTp.h"
#include "Diag.h"
#include "Com.h"


/***********************************************************************************************************************
**                                           Global Variable Definitions                                              **
***********************************************************************************************************************/


/***********************************************************************************************************************
**                                            Private Macro Definitions                                               **
***********************************************************************************************************************/
/*ACAN*/
#define CANTP_COM_BUS_ID                COM_CHANNEL_ACAN

#define CANTP_PCI_TYPE_MASK             ((uint8)0xF0)
#define CANTP_PCI_TYPE_SF               ((uint8)0x00)
#define CANTP_PCI_TYPE_FF               ((uint8)0x10)
#define CANTP_PCI_TYPE_CF               ((uint8)0x20)
#define CANTP_PCI_TYPE_FC               ((uint8)0x30)

#define CANTP_PCI_CF_SN_LIMIT           (15U)

/* unit:10ms */
#define CANTP_TX_CONFIRMATION_TIMEOUT 	(10U)

/***********************************************************************************************************************
**                                            Private Type Definitions                                                **
***********************************************************************************************************************/
/*CanTp frame type*/
typedef enum
{
    CANTP_FRAMETYPE_NULL = 0U,
    CANTP_FRAMETYPE_RX_CTRL_SF,
    CANTP_FRAMETYPE_RX_CTRL_FF,
    CANTP_FRAMETYPE_RX_CTRL_CF,
    CANTP_FRAMETYPE_RX_CTRL_FC,
    CANTP_FRAMETYPE_TX_CTRL_SF,
    CANTP_FRAMETYPE_TX_CTRL_FF,
    CANTP_FRAMETYPE_TX_CTRL_CF,
    CANTP_FRAMETYPE_TX_CTRL_FC
} CanTp_FrameType;

typedef struct
{
    CanTp_FrameType frameType;
    uint8 dataIndex;//current write index of data[200].
    uint8 dataLength;//the whole data length of this request.
    uint8 data[200];
} CanTp_BufferType;

/*CanTp runtime status*/
typedef enum
{
    CANTP_STATUS_IDLE = 0U,
    CANTP_STATUS_RX,
    CANTP_STATUS_TX
} CanTp_StatusType;

/*CanTp runtime control block*/
typedef struct
{
    CanTp_StatusType status;
    uint8 txCFCurSN;
    uint8 txCFCurBS;
    uint8 txCFFrameCnt;
    uint8 txConfirmationTimeoutFlag;
	uint8 txConfirmationCnt;
} CanTp_CtrlType;

/***********************************************************************************************************************
**                                           Private Variable Definitions                                             **
***********************************************************************************************************************/
static CanTp_CtrlType CanTp_Ctrl;
static CanTp_BufferType CanTp_RxBuffer;
static CanTp_BufferType CanTp_TxBuffer;

/***********************************************************************************************************************
**                                           Private Function Declarations                                            **
***********************************************************************************************************************/
/*receive handle*/
static void CanTp_SFRxIndication(uint8 length, uint8 const* SduDataPtr);
static void CanTp_FFRxIndication(uint8 length, uint8 const* SduDataPtr);
static void CanTp_CFRxIndication(uint8 length, uint8 const* SduDataPtr);
/*send specific FC frame*/
static void CanTp_SendFC(void);

/*transmit handle*/
static void CanTp_SendSF(void);
static void CanTp_SendFF(void);
static void CanTp_SendCF(void);
static void CanTp_FCRxIndication(uint8 length, uint8 const* SduDataPtr);
static void CanTp_ResetTxConfirmationParam(void); 
/*when tx confirmation timeout, force confirm*/
static void CanTp_TxConfirmationTimeoutProcess(void);
/*tx confirmation timeout cnt self dec*/
static void CanTp_TxConfirmationTimeoutCntSelfDec(void);

/***********************************************************************************************************************
**                                            Global Function Definitions                                             **
***********************************************************************************************************************/
/*This function initializes the CanTp module*/
void CanTp_Init(void)
{
    CanTp_Ctrl.status = CANTP_STATUS_IDLE;
    CanTp_Ctrl.txCFCurBS = 0;
    CanTp_Ctrl.txCFCurSN = 0;
    CanTp_Ctrl.txCFFrameCnt = 0;
    CanTp_Ctrl.txConfirmationTimeoutFlag = FALSE;
	CanTp_Ctrl.txConfirmationCnt = CANTP_TX_CONFIRMATION_TIMEOUT;
    /*clear buffer*/
    memset(&CanTp_RxBuffer, 0, sizeof(CanTp_RxBuffer));
    memset(&CanTp_TxBuffer, 0, sizeof(CanTp_TxBuffer));
}

/*Requests transmission of a PDU*/
Std_ReturnType CanTp_Transmit(uint8 length, uint8 const* SduDataPtr)
{
    Std_ReturnType ret = E_OK;

    if (CANTP_STATUS_IDLE == CanTp_Ctrl.status)
    {
        CanTp_Ctrl.status = CANTP_STATUS_TX;
        memcpy(&(CanTp_TxBuffer.data[0]), &(SduDataPtr[0]), length);
        CanTp_TxBuffer.dataIndex = 0;
        CanTp_TxBuffer.dataLength = length;
        if (length <= 7u)
        {
            CanTp_SendSF();
        }
        else
        {
            CanTp_SendFF();
        }
        
        CanTp_Ctrl.txConfirmationTimeoutFlag = TRUE;
		CanTp_Ctrl.txConfirmationCnt = CANTP_TX_CONFIRMATION_TIMEOUT;
    }
    else
    {
        ret = E_NOT_OK;
    }
    
    return ret;
}


/*Indication of a received N-PDU from a lower layer communication interface module*/
void CanTp_RxIndication(uint32 canId, uint8 length, uint8 const* SduDataPtr)
{
    uint8 frameType;
    frameType = (uint8)(SduDataPtr[0] & CANTP_PCI_TYPE_MASK);
    switch (frameType)
    {
    case CANTP_PCI_TYPE_SF:
        /*a single frame received*/
        CanTp_SFRxIndication(length, SduDataPtr);
        break;
    case CANTP_PCI_TYPE_FF:
        /*a first frame received*/
        CanTp_FFRxIndication(length, SduDataPtr);
        break;
    case CANTP_PCI_TYPE_CF:
        /*a consecutive frame received*/
        CanTp_CFRxIndication(length, SduDataPtr);
        break;
    case CANTP_PCI_TYPE_FC:
        /*a flow control frame received*/
        CanTp_FCRxIndication(length, SduDataPtr);
        break;
    default:
        /*invalid frame type, ignore it*/
        break;
    }
}


/*The lower layer communication interface module confirms the transmission of a PDU*/
void CanTp_TxConfirmation(void)
{
    if (CANTP_STATUS_TX == CanTp_Ctrl.status)
    {
        if (CANTP_FRAMETYPE_TX_CTRL_SF == CanTp_TxBuffer.frameType)
        {
            CanTp_Ctrl.status = CANTP_STATUS_IDLE;
            /*clear buffer*/
            memset(&CanTp_TxBuffer, 0, sizeof(CanTp_TxBuffer));
            /*notify upper*/
            Diag_TxConfirmation();
            CanTp_ResetTxConfirmationParam();
        }
        else if (CANTP_FRAMETYPE_TX_CTRL_CF == CanTp_TxBuffer.frameType)
        {
            if (CanTp_TxBuffer.dataIndex >= CanTp_TxBuffer.dataLength)
            {
                CanTp_Ctrl.status = CANTP_STATUS_IDLE;
                /*clear buffer*/
                memset(&CanTp_TxBuffer, 0, sizeof(CanTp_TxBuffer));
                /*notify upper*/
                Diag_TxConfirmation();
                CanTp_ResetTxConfirmationParam();
            }
            else
            {
                if (CanTp_Ctrl.txCFFrameCnt < CanTp_Ctrl.txCFCurBS)
                {
                    CanTp_SendCF();
                    CanTp_ResetTxConfirmationParam();
                }
            }
        }
        else
        {
            /*do nothing*/
        }
    }
}

/**
  * @brief  CanTp schdule table
  * @param  void
  * @retval void
  */
void CanTp_MainFunction(void)
{
    /*tx confirmation timeout cnt self dec*/
    CanTp_TxConfirmationTimeoutCntSelfDec();
    /*when tx confirmation timeout, force confirm*/
    CanTp_TxConfirmationTimeoutProcess();
}

/***********************************************************************************************************************
**                                           Private Function Definitions                                             **
***********************************************************************************************************************/
static void CanTp_ResetTxConfirmationParam(void)
{
	CanTp_Ctrl.txConfirmationTimeoutFlag = FALSE;
	CanTp_Ctrl.txConfirmationCnt = CANTP_TX_CONFIRMATION_TIMEOUT;
}

/*when tx confirmation timeout, force confirm*/
static void CanTp_TxConfirmationTimeoutProcess(void)
{
	if (TRUE == CanTp_Ctrl.txConfirmationTimeoutFlag)
	{
		if (0 == CanTp_Ctrl.txConfirmationCnt)
		{
			Diag_TxConfirmation();
			CanTp_ResetTxConfirmationParam();
			CanTp_Ctrl.status = CANTP_STATUS_IDLE;
		}
		else
		{
			/* nop */
		}
	}
	else
	{
		/* nop */
	}
}

/*tx confirmation timeout cnt self dec*/
static void CanTp_TxConfirmationTimeoutCntSelfDec(void)
{
	if (TRUE == CanTp_Ctrl.txConfirmationTimeoutFlag)
	{
		if (CanTp_Ctrl.txConfirmationCnt > 0)
		{
			CanTp_Ctrl.txConfirmationCnt--;
		}
		else
		{
			/* nop */
		}
	}
	else
	{
		/* nop */
	}
}

static void CanTp_SFRxIndication(uint8 length, uint8 const* SduDataPtr)
{
    uint8 frameOffset;
    uint8 curPduDataLen;
    
    /*cantp enter rx mode*/
    CanTp_Ctrl.status = CANTP_STATUS_RX;
    
    /*get N_PCI*/
    frameOffset = 0;
    curPduDataLen = SduDataPtr[frameOffset] & 0x0Fu;
    
    frameOffset = 1u;
    /*drop the frame 3e 80 for test.*/
    if((0x3E == SduDataPtr[frameOffset]) && (0x80 == SduDataPtr[frameOffset + 1]))
    {
        /*cantp quit rx mode*/
        CanTp_Ctrl.status = CANTP_STATUS_IDLE;
    }
    else
    {
        /*copy data*/
        CanTp_RxBuffer.frameType = CANTP_FRAMETYPE_RX_CTRL_SF;
        CanTp_RxBuffer.dataLength = curPduDataLen;
        memcpy(&(CanTp_RxBuffer.data[0]), &(SduDataPtr[frameOffset]), curPduDataLen);
        
        /*cantp quit rx mode*/
        CanTp_Ctrl.status = CANTP_STATUS_IDLE;
        
        /*notify upper*/
        Diag_RxIndication(CanTp_RxBuffer.dataLength, &(CanTp_RxBuffer.data[0]));
        
        /*clear buffer*/
        memset(&CanTp_RxBuffer, 0, sizeof(CanTp_RxBuffer));
    }
    

}

static void CanTp_FFRxIndication(uint8 length, uint8 const* SduDataPtr)
{
    uint8  frameOffset;
    uint8  curPduDataLen;
    uint16 dataLen;
    
    /*cantp enter rx mode*/
    CanTp_Ctrl.status = CANTP_STATUS_RX;
    
    /*get N_PCI*/
    frameOffset = 0;
    dataLen = SduDataPtr[frameOffset] & 0x0Fu;
    dataLen <<= 8u;
    frameOffset = 1u;
    dataLen |= SduDataPtr[frameOffset];
    
    frameOffset = 2u;
    /*copy data*/
    CanTp_RxBuffer.frameType = CANTP_FRAMETYPE_RX_CTRL_FF;
    CanTp_RxBuffer.dataLength = dataLen;
    curPduDataLen = length - frameOffset;
    /*copy data*/
    memcpy(&(CanTp_RxBuffer.data[0]), &(SduDataPtr[frameOffset]), curPduDataLen);
    CanTp_RxBuffer.dataIndex = curPduDataLen;
    
    /*send flow control frame*/
    CanTp_SendFC();
}

static void CanTp_CFRxIndication(uint8 length, uint8 const* SduDataPtr)
{
    uint8 frameOffset;
    uint8 curPduDataLen;
    uint8 sn;
    
    /*get N_PCI*/
    frameOffset = 0;
    sn = SduDataPtr[frameOffset] & 0x0Fu;
    
    frameOffset = 1u;
    /*copy data*/
    CanTp_RxBuffer.frameType = CANTP_FRAMETYPE_RX_CTRL_CF;
    curPduDataLen = length - frameOffset;
    /*copy data*/
    memcpy(&(CanTp_RxBuffer.data[CanTp_RxBuffer.dataIndex]), &(SduDataPtr[frameOffset]), curPduDataLen);
    CanTp_RxBuffer.dataIndex += curPduDataLen;
    
    if (CanTp_RxBuffer.dataIndex >= CanTp_RxBuffer.dataLength)
    {
        /*cantp quit rx mode*/
        CanTp_Ctrl.status = CANTP_STATUS_IDLE;
        
        /*notify upper*/
        Diag_RxIndication(CanTp_RxBuffer.dataLength, &(CanTp_RxBuffer.data[0]));
        
        /*clear buffer*/
        memset(&CanTp_RxBuffer, 0, sizeof(CanTp_RxBuffer));
    }
    else
    {
        /*when the complete msg has not been received, the FC msg should be checked and send
         *SN = 0~15, when SN = 0 or 8.*/
        if (0 == (sn % 8u))
        {
            CanTp_SendFC();
        }
    }
}

/*send specific FC frame*/
static void CanTp_SendFC(void)
{
    uint8 npduData[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
    
    CanTp_RxBuffer.frameType = CANTP_FRAMETYPE_RX_CTRL_FC;

    /*set N_PCI*/
    npduData[0] = CANTP_PCI_TYPE_FC;
    npduData[1] = 8u; /*set BS*/
    npduData[2] = 0; /*set STmin*/
    Com_SendCanData(CANTP_COM_BUS_ID, CANTP_DIAG_Resp_ID, npduData, 3u);
}



static void CanTp_SendSF(void)
{
    uint8 npduData[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};

    CanTp_TxBuffer.frameType = CANTP_FRAMETYPE_TX_CTRL_SF;

    /*set N_PCI*/
    npduData[0] = (uint8)(CANTP_PCI_TYPE_SF | CanTp_TxBuffer.dataLength);
    /*copy data*/
    memcpy(&(npduData[1]), &(CanTp_TxBuffer.data[0]), CanTp_TxBuffer.dataLength);
    /*send pdu*/
    Com_SendCanData(CANTP_COM_BUS_ID, CANTP_DIAG_Resp_ID, npduData, CanTp_TxBuffer.dataLength + 1u);
}

static void CanTp_SendFF(void)
{
    uint8 npduData[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
    uint8 curPduDataLen;

    CanTp_TxBuffer.frameType = CANTP_FRAMETYPE_TX_CTRL_FF;

    /*set N_PCI*/
    npduData[0] = (uint8)(CANTP_PCI_TYPE_FF | (uint8)(0x0Fu & (CanTp_TxBuffer.dataLength >> 8u)));
    npduData[1] = (uint8)(CanTp_TxBuffer.dataLength);
    /*the FF shall be treated as the segment number 0.*/
    CanTp_Ctrl.txCFCurSN = 0;
    /*copy data*/
    curPduDataLen = 6u;
    memcpy(&(npduData[2]), &(CanTp_TxBuffer.data[0]), curPduDataLen);
    /*send pdu*/
    Com_SendCanData(CANTP_COM_BUS_ID, CANTP_DIAG_Resp_ID, npduData, curPduDataLen + 2u);
    CanTp_TxBuffer.dataIndex = curPduDataLen;
}

static void CanTp_SendCF(void)
{
    uint8 npduData[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
    uint8 curPduDataLen;

    CanTp_TxBuffer.frameType = CANTP_FRAMETYPE_TX_CTRL_CF;

    /*set N_PCI*/
    CanTp_Ctrl.txCFCurSN++;
    if (CanTp_Ctrl.txCFCurSN > CANTP_PCI_CF_SN_LIMIT)
    {
        CanTp_Ctrl.txCFCurSN = 0;
    }
    npduData[0] = (uint8)(CANTP_PCI_TYPE_CF | CanTp_Ctrl.txCFCurSN);
    /*copy data*/
    curPduDataLen = CanTp_TxBuffer.dataLength - CanTp_TxBuffer.dataIndex;
    if (curPduDataLen > 7u)
    {
        curPduDataLen = 7u;
    }
    memcpy(&(npduData[1]), &(CanTp_TxBuffer.data[CanTp_TxBuffer.dataIndex]), curPduDataLen);
    /*send pdu*/
    Com_SendCanData(CANTP_COM_BUS_ID, CANTP_DIAG_Resp_ID, npduData, curPduDataLen + 1u);
    /*update control info*/
    CanTp_TxBuffer.dataIndex += curPduDataLen;
    CanTp_Ctrl.txCFFrameCnt++;
}

static void CanTp_FCRxIndication(uint8 length, uint8 const* SduDataPtr)
{
    CanTp_TxBuffer.frameType = CANTP_FRAMETYPE_TX_CTRL_FC;

    /*get N_PCI*/
//    fs = (uint8)(SduDataPtr[0] & 0x0Fu);
    CanTp_Ctrl.txCFCurBS = SduDataPtr[1];
//    stmin = SduDataPtr[2];

    /*clear consecutive frame count.*/
    CanTp_Ctrl.txCFFrameCnt = 0;

    CanTp_SendCF();
}



/***********************************************************************************************************************
**                                                  General Notes                                                     **
***********************************************************************************************************************/

